#pragma once

#include "TrackManager.h"
#include "../Core/Synchronizer.h"

template <class T>
class SynchronizedTrackManager : public TrackManager<T>
{
protected:
	InternetAddress m_SynchronizedAddress;
	BufferedSocket m_SocketSynchronized;

	virtual void ExecuteByInterval()
	{
		TrackManager<T>::ExecuteByInterval();
		if(Synchronizer::GetSingleton().IsPrimary())
		{
			m_SocketSynchronized.Close();
		}
		else
		{
			while(!m_SocketSynchronized.IsReceiverEmpty())
			{
				T* t_T = new T(this);
				ReceiveSynchronized(t_T);
				ForceUpdate(t_T);
			}
		}
	}

public:
	SynchronizedTrackManager(bool p_OverWrite, InternetAddress p_LocalAddress, InternetAddress p_BroadcastAddress, void* p_Parameter = NULL, InternetAddress pMulticastGroup = InternetAddress("" , 0)) : TrackManager<T>(p_OverWrite, p_LocalAddress, p_BroadcastAddress, p_Parameter, pMulticastGroup), m_SocketSynchronized(Socket(Protocol_Udp))
	{
		if(p_BroadcastAddress.PortNumber!=0)
		{
			m_SynchronizedAddress.PortNumber = p_BroadcastAddress.PortNumber+1;
			m_SocketSynchronized.Bind(m_SynchronizedAddress);
			m_SynchronizedAddress.Address = "127.0.0.1";
			m_SocketSynchronized.StartReceiver();
		}
	}

	virtual void Synchronize()
	{
		for(typename std::map<unsigned short, T*>::iterator p=std::map<unsigned short, T*>::begin(); p!=std::map<unsigned short, T*>::end(); p++)
		{
			SendSynchronized(p->second);
		}
	}

	virtual void ForceUpdate(T* p_T)
	{
		if(p_T==NULL)
		{
			return;
		}
		if(p_T->IsDeleteMessage())
		{
			unsigned short t_TrackNumber = p_T->GetTrackNumber();
			T* t_T = TrackManager<T>::GetTrack(t_TrackNumber);
			if((t_TrackNumber!=0) && (t_T!=NULL) && BeforeDelete(*t_T))
			{
				std::map<unsigned short, T*>::erase(t_TrackNumber);
				delete t_T;
			}
			delete p_T;
			return;
		}
		unsigned short t_TrackNumber = p_T->GetTrackNumber();
		if(t_TrackNumber==0)
		{
			delete p_T;
			return;
		}
		T* t_T = TrackManager<T>::GetTrack(t_TrackNumber);
		if(t_T!=NULL)
		{
			t_T->Reset();
			t_T->SynchronizeFrom(*p_T);
			delete p_T;
			return;
		}
		else
		{
			std::map<unsigned short, T*>::insert(std::pair<unsigned short, T*>(t_TrackNumber, p_T));
		}
	}

	virtual void ReceiveSynchronized(T* p_T)
	{
		if(!Synchronizer::GetSingleton().IsPrimary())
		{
			try
			{
				RawData t_RawData;
				m_SocketSynchronized.Receive(t_RawData);
				p_T->ReadRawData(t_RawData);
			}
			catch(...)
			{
				Log::GetSingleton().Write(LogLevel_Error, "Error while receiving synchronized data");
			}
		}
	}

	virtual void SendSynchronized(T* p_T)
	{
		try
		{
			RawData t_RawData;
			p_T->WriteRawData(t_RawData);
			TrackManager<T>::m_Socket.SendTo(t_RawData, m_SynchronizedAddress);
		}
		catch(...)
		{
			Log::GetSingleton().Write(LogLevel_Error, "Error while sending synchronized data");
		}
	}
};
